#include <stdlib.h>
#include <math.h>
#include <iostream>
#include <vector>
#include "windows.h"
#include "TextDrawer.h"
#include "TextureDrawer.h"
#include "Track.h"
#include "Buildings.h"
#include "Balls.h"
#include "Bike.h"
#include "Background.h"
#include "imageloader.h"
#include "vec3f.h"
#include "ReadScores.h"
#include "WriteScores.h"

#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

//XYZ point where camera looks
float centerX=0,centerY=0,centerZ=-10000000;
// XYZ position of the camera
float eyeX=0.0f,eyeY=10.0f,eyeZ=20.0f;
//bike position
float bikeXpos=0.0f, bikeYPos = 1.5, bikeZpos=0;
//bike size
float bikeHeight=3, bikeWidth = 2, bikeLength = 8;

int offsetFromDistanceTravelled;
float xBallOffSets[100];

float speed = .1f;
bool isMoving = true;
float ballPosition[5][3];
int numberOfCollision =0;
int currentBall;
int nextBall;
int score;
bool thirdPerson = false;
int gameState = 0;

bool Up = false;
bool Down = false;
bool Left = false;
bool Right = false;

char* textureImageNames[8] = {"snow-camo.bmp", "nightbuilding1.bmp", "tire.bmp", "leather1.bmp", "red-light.bmp", "yellow-light.bmp", "chrome1.bmp", "chrome2.bmp"};

TextDrawer text;
Track *track;
Buildings *buildings;
Balls *balls;
Bike *bike;
Background *background;
TextureDrawer *textures;
	

//textures
GLuint backgroundID;
Image* backgroundIMG;

void changeSize(int w, int h) {

	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if (h == 0)
		h = 1;
	float ratio =  w * 1.0 / h;

	// Use the Projection Matrix
	glMatrixMode(GL_PROJECTION);

	// Reset Matrix
	glLoadIdentity();

	// Set the viewport to be the entire window
	glViewport(0, 0, w, h);

	// Set the correct perspective.
	gluPerspective(45.0f, ratio, 0.1f, 1000.0f);

	// Get Back to the Modelview
	glMatrixMode(GL_MODELVIEW);
}

void detectBalltoBikeCollisions() 
{
	float bl,br,bt,bb, ml,mr,mt,mb;
	br=balls->position[1][0]+0.5;
	bl=balls->position[1][0]-0.5;
	bt=balls->position[1][2]-0.5;
	bb=balls->position[1][2]+0.5;

	ml=bikeXpos-bikeWidth/2 ;
	mr=bikeXpos+bikeWidth/2;
	mt=bikeZpos-bikeLength/2-15;
	mb=bikeZpos+bikeLength/2-15;
	int testBall= balls->position[1][2];
	if(mt<bb && mb>bt && mr>bl && ml<br){
		if(currentBall != testBall){
			currentBall = balls->position[1][2];
			numberOfCollision++;

			speed-=4.0f;
			if(speed<=0.1f){
				speed=0.1f;	
			}
		}
	}
}
void drawBike(float Alpha){
	bike->draw(Alpha,0,1,0,0,90,0); // alpha = first parameter
} 
void drawBikeWithAlphaBlending() 
{
	//draw bike
	glPushMatrix();

	glTranslatef(bikeXpos,-1,bikeZpos-15);
	glColor4f(1,0,0,0.1f);
	glScalef(bikeWidth*2,bikeHeight,bikeLength);
	drawBike(1.0f);

	//blending
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glEnable(GL_ALPHA_TEST);
	if(speed>1.0){
		glScalef(1,1.000f,1.0f);
		glTranslatef(0,-.0f,0.025f);
		drawBike(.4f);
	}
	if(speed>2.0){
		glScalef(1,1.000f,1.0f);
		glTranslatef(0,-.0f,0.025f);
		drawBike(.2f);

	}
	if(speed>3.0){
		glScalef(1,1.000f,1.0f);
		glTranslatef(0,-.0f,0.025f);
		drawBike(.1f);

	}

	glDisable(GL_ALPHA_TEST);
	glDisable(GL_BLEND);

	glPopMatrix();
}

//texture
GLuint loadTexture(Image* image) {
	GLuint textureId;
	glGenTextures(1, &textureId);
	glBindTexture(GL_TEXTURE_2D, textureId);
	glTexImage2D(GL_TEXTURE_2D,
				 0,
				 GL_RGB,
				 image->width, image->height,
				 0,
				 GL_RGB,
				 GL_UNSIGNED_BYTE,
				 image->pixels);
	return textureId;
}

// Load the TextDrawer object with images and texture ids
void loadTextureDrawer(){
	std::vector<Image*> images;
	std::vector<GLuint> ids;
	int numberOfTextures = 0;

	for each (char* name in textureImageNames){
		images.push_back(loadBMP(name));
		ids.push_back(loadTexture(images[numberOfTextures]));
		numberOfTextures++;
	}

	textures = new TextureDrawer(images, ids, numberOfTextures);
}

void createLight(){
	glEnable(GL_LIGHTING);
	GLfloat red[] = {1.0, 0.0, 0.0, 1.0};
	GLfloat green[] = {0.0, 1.0, 0.0, 1.0};
	GLfloat white[] = {1.0, 1.0, 1.0, 1.0};
	GLfloat white2[] = {1.0, 1.0, 1.0, 0.5};
	GLfloat yellow[] = {1.0, 1.0, 0.0, 1.0};
	GLfloat crimson[] = {220.0/255.0, 20.0/255.0, 60.0/255.0, 1.0};
	GLfloat moonLight[] = {25.0/255.0, 25.0/255.0, 112.0/255.0, 0.5};
	GLfloat sunLight[] = {255.0/255.0, 193.0/255.0, 37.0/255.0, 1.0};
	GLfloat position[] = {0,0,100000};
	glLightfv (GL_LIGHT1, GL_DIFFUSE, moonLight);
	glLightfv (GL_LIGHT1, GL_POSITION, position);
	glEnable( GL_LIGHT1 );


	GLfloat position2[] = {bikeXpos,10,bikeZpos};
	GLfloat direction2[] = {0,-10,-10};

	glLightfv(GL_LIGHT2, GL_POSITION, position2);
	glLightfv (GL_LIGHT2, GL_SPOT_CUTOFF, white);
	glLightfv (GL_LIGHT2, GL_DIFFUSE, yellow);
	glLightf(GL_LIGHT2, GL_SPOT_CUTOFF,15);
	glLightf(GL_LIGHT2, GL_SPOT_EXPONENT, 90);
	glEnable(GL_LIGHT2);

	glLightfv (GL_LIGHT3, GL_DIFFUSE, white2);
	glEnable(GL_LIGHT3);

}


void renderScene(void) {

	
	// Clear Color and Depth Buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(25.0/255.0, 25.0/255.0, 112.0/255.0,1);//midnight blue
	// Reset transformations

	glLoadIdentity();
	

	if (gameState == 1)
	{
		glPushMatrix();
		glColor3f(1,1,1);
		text.drawScore(score,-10,39,-100);
		text.drawNumOfCollisions(numberOfCollision,-10,36.5f,-100);

		glPopMatrix();
		if (Left)
		{
			if(bikeXpos>=-(track->width/2 - 2)){
				bikeXpos-=0.1 + speed / 50;
			}
		}
		if (Right)
		{
			if(bikeXpos<=(track->width/2 - 2)){
				bikeXpos+=0.1 + speed / 50;
			}
		}
		if (Up)
		{
			speed+=0.01;
		}
		if (Down)
		{
			speed-=0.01;
		}


		// Set the camera
		if (thirdPerson)
		{
			gluLookAt(	eyeX, eyeY, eyeZ+bikeZpos-5-speed*6.0f/10.0f,
				centerX, centerY,  centerZ,
				0.0f, 1.0f,  0.0f);
		}
		else
		{
			//XYZ point where camera looks
			centerX=0,centerY=0,centerZ=-10000000;
			// XYZ position of the camera
			eyeX=0.0f,eyeY=10.0f,eyeZ=20.0f;
			gluLookAt(	eyeX + bikeXpos, eyeY - 5.0f, eyeZ+bikeZpos-33.0f-speed*6.0f/10.0f,// -speed,
				centerX, centerY,  centerZ,
				0.0f, 1.0f,  0.0f);
		}
		if(isMoving){
			bikeZpos += (-speed);
			if(speed<=25){
				speed+=0.005f;
			}
			else{
				speed =25.0f;
			}
		}

		offsetFromDistanceTravelled = -(bikeZpos-1)/10;
		track->draw(offsetFromDistanceTravelled);
		buildings->draw(track->width, offsetFromDistanceTravelled);

		offsetFromDistanceTravelled = -(bikeZpos-1)/100;
		balls->draw(offsetFromDistanceTravelled);
		detectBalltoBikeCollisions();
		drawBikeWithAlphaBlending();
	
		background->draw(track->length, eyeZ, bikeZpos, backgroundIMG, backgroundID);
		
		score = -bikeZpos;
	}
	else if (gameState == 0)
	{
		//XYZ point where camera looks
		centerX=0,centerY=0,centerZ=-10000000;
		// XYZ position of the camera
		eyeX=0.0f,eyeY=10.0f,eyeZ=20.0f;
		//bike position
		bikeXpos=0, bikeYPos = 1.5, bikeZpos=0;

		gluLookAt(	eyeX, eyeY, eyeZ+bikeZpos-speed,// -speed,
				centerX, centerY,  centerZ,
				0.0f, 1.0f,  0.0f);
		background->draw(800, 640, backgroundIMG, backgroundID);

		if (score != 0)
		{
			WriteScores write;
			write.draw(score);
		}

		ReadScores read;
		read.draw(0, 0);

		score = 0;

	}
	else if (gameState == 4)
	{
		//XYZ point where camera looks
		centerX=0,centerY=0,centerZ=-10000000;
		// XYZ position of the camera
		eyeX=0.0f,eyeY=10.0f,eyeZ=20.0f;
		//bike position
		bikeXpos=0, bikeYPos = 1.5, bikeZpos=0;

		gluLookAt(	eyeX, eyeY, eyeZ+bikeZpos-speed,// -speed,
				centerX, centerY,  centerZ,
				0.0f, 1.0f,  0.0f);
		background->draw(800, 640, backgroundIMG, backgroundID);

		if (score != 0)
		{
			WriteScores write;
			write.draw(score);
		}

		ReadScores read;
		read.draw(0, 0);

		score = 0;

	}

	if (numberOfCollision >= 3)
	{
		gameState = 4;
		backgroundIMG = loadBMP("game over.bmp");
		backgroundID = loadTexture(backgroundIMG);
		delete backgroundIMG;
		numberOfCollision=0;
		speed=0.1f;
	}


	glFlush();	
	glutSwapBuffers();
}

void pressNormalKeys(unsigned char key, int x, int y) {
	switch(key){
		// Z axis translation of camera
		case 'b':
			//eyeZ += 10;
			break;
		case 'f':
			//eyeZ -= 10;
			break;

		case '1':
			thirdPerson = false;
			break;
		case '3':
			//thirdPerson = true;
			break;
	}

	if (key == 13 && gameState == 0)
	{
		gameState = 1;
		backgroundIMG = loadBMP("image1.bmp");
		backgroundID = loadTexture(backgroundIMG);
		delete backgroundIMG;		
	}

	else if (key == 13 && gameState == 4)
	{
		gameState = 1;
		backgroundIMG = loadBMP("image1.bmp");
		backgroundID = loadTexture(backgroundIMG);
		delete backgroundIMG;		
	}
	
	else if (key == 27 && gameState == 1)
	{
		gameState = 0;
		backgroundIMG = loadBMP("menu.bmp");
		backgroundID = loadTexture(backgroundIMG);
		delete backgroundIMG;
		numberOfCollision = 0;
		speed = 0.1f;
	}

	else if (key == 27 && gameState == 4)
	{
		gameState = 0;
		backgroundIMG = loadBMP("menu.bmp");
		backgroundID = loadTexture(backgroundIMG);
		delete backgroundIMG;
	}

	else if (key == 27 && gameState == 0)
	{
		exit(0);
	}

	if(key== 'd'){
			
		Right = true;
	}
	if(key== 'a'){
		Left = true;
	}
	if(key== 'q'){
		isMoving=!isMoving;
	}

	// Switch between bike textures and colors
	if(key == 't'){
		bike->changeShowTexture();
	}
	if(key == 'T'){
		bike->changeShowTexture();
	}

	// Switch between building textures and colors
	if(key == 'y'){
		buildings->changeShowTexture();
	}
	if(key == 'Y'){
		buildings->changeShowTexture();
	}
}

void releaseNormalKeys(unsigned char key, int x, int y) {
	if(key== 's'){
		Down = false;
	}
	if(key== 'w'){
		Up = false;
	}
	if(key== 'd'){
			
		Right = false;
	}
	if(key== 'a'){
		Left = false;
	}

}

void processSpecialKeys(int key, int xx, int yy) {
	float angle= 0.1;
	float x2 = 0.0f, y2 =0.0f, z2=0.0f;
	// X and Y translation of camera
	/*
	switch (key) {
	case GLUT_KEY_LEFT :
		x2 = eyeX*cos(angle) - eyeZ*sin(angle);
		z2 = eyeX*sin(angle) + eyeZ*cos(angle); 
		eyeX=x2;
		eyeZ=z2;
		break;
	case GLUT_KEY_RIGHT :
		x2 = eyeX*cos(-angle) - eyeZ*sin(-angle);
		z2 = eyeX*sin(-angle) + eyeZ*cos(-angle); 
		eyeX=x2;
		eyeZ=z2;
		break;
	case GLUT_KEY_UP :
		y2 = eyeY*cos(angle) - eyeZ*sin(angle);
		z2 = eyeY*sin(angle) + eyeZ*cos(angle); 
		eyeY=y2;
		eyeZ=z2;
		break;
	case GLUT_KEY_DOWN :

		y2 = eyeY*cos(-angle) - eyeZ*sin(-angle);
		z2 = eyeY*sin(-angle) + eyeZ*cos(-angle); 
		eyeY=y2;
		eyeZ=z2;
		break;
	}
	*/
}

int main(int argc, char **argv) {

	// init GLUT and create window
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(0,0);
	glutInitWindowSize(800,640);
	glutCreateWindow("comp371 project");
	glEnable(GL_DEPTH_TEST);
	createLight();
	glEnable(GL_COLOR_MATERIAL);
	glShadeModel(GL_SMOOTH);
	glEnable (GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

	// Call this before declaring objects that require TextureDrawer
	loadTextureDrawer(); 

	track = new Track(20,500);
	buildings = new Buildings(10, textures);
	balls = new Balls();
	bike = new Bike(2, textures);

	if (gameState == 1) //game
	{
		backgroundIMG = loadBMP("image1.bmp");
		backgroundID = loadTexture(backgroundIMG);
		delete backgroundIMG;
	}
	else if (gameState == 0) //menu
	{
		backgroundIMG = loadBMP("menu.bmp");
		backgroundID = loadTexture(backgroundIMG);
		delete backgroundIMG;
	}

	for(int i =0;i<100;i++){
		xBallOffSets[i] = rand()%9-5.5;
	}
	// register callbacks
	glutDisplayFunc(renderScene);
	glutReshapeFunc(changeSize);
	glutIdleFunc(renderScene);

	// here are the new entries
	glutKeyboardFunc(pressNormalKeys);
	glutKeyboardUpFunc(releaseNormalKeys);
	glutSpecialFunc(processSpecialKeys);

	// enter GLUT event processing cycle
	glutMainLoop();

	return 1;
}